package org.jspar.temp;

import java.util.ArrayList;
import java.util.List;

import org.jspar.model.Module;
import org.jspar.model.Net;
import org.jspar.model.NetList;
import org.jspar.model.Terminal;
import org.jspar.tile.DefaultTileDelegate;
import org.jspar.tile.Rectangle;
import org.jspar.tile.Tile;

public class GlobalRouting {
	
	private List<Expansion> allExpansions = new ArrayList<Expansion>();
	private List<Expansion> activeExpansions = new ArrayList<Expansion>();
	
	private Tile hRoot;
	private Tile vRoot;
	
	public void run(NetList netList) {
		//buildTileSpaces(netList);
		//makeInitialExpansions(netList);
		while (activeExpansions.size() > 0) {
			processAllExpansions();
		}
		for (Expansion e : allExpansions) {
			e.resetPathCost();
		}
		activeExpansions.addAll(allExpansions);
		while (activeExpansions.size() > 0) {
			processAllExpansions();
			for (Expansion e : activeExpansions) {
				e.resetPathCost();
			}
		}
		activeExpansions.addAll(allExpansions);
	}
	
	private void processAllExpansions() {
		List<Expansion> expns = new ArrayList<Expansion>(activeExpansions);
		for (Expansion e : expns) {
			if (activeExpansions.contains(e))
				makeNextMove(e);
		}
	}

	public void buildTileSpaces(NetList netList) {
		hRoot = new Tile(-10, -10, 100, 70, DefaultTileDelegate.INSTANCE);
		vRoot = new Tile(-10, -10, 70, 100, DefaultTileDelegate.INSTANCE);
		for (Module m : netList.modules()) {
			Rectangle r = m.getBounds();
			hRoot.insertTile(r.x(), r.y(), r.width(), r.height(), new TileInfo(m, true));
			vRoot.insertTile(r.y(), r.x(), r.height(), r.width(), new TileInfo(m, false));
		}
		for (Tile tl : hRoot.allspace()) {
			if (tl.content() == null)
				tl.setContent(new TileInfo(tl, true));
		}
		for (Tile tl : vRoot.allspace()) {
			if (tl.content() == null)
				tl.setContent(new TileInfo(tl, false));
		}
	}

	public void makeInitialExpansions(NetList netList) {
		for (Net n : netList.nets()) {
			List<Expansion> sibling = new ArrayList<Expansion>();
			for (Terminal t : n.terminals()) {
				Tile tile = findNearestTile(t);
				Expansion e = new Expansion(t, tile, sibling);
				sibling.add(e);
				allExpansions.add(e);
				if (e.touchingExpansion().isEmpty()) {
					activeExpansions.add(e);
				}
			}
		}
	}
	
	private Tile findNearestTile(Terminal t) {
		if (t.side() == Terminal.UP || t.side() == Terminal.DOWN)
			return vRoot.locate(t.getGlobalPosition());
		return hRoot.locate(t.getGlobalPosition());
	}

	private void makeNextMove(Expansion e) {
		List<Expansion> end = e.touchingExpansion();
		if (end.size() > 0) {
			activeExpansions.remove(e);
			for (Expansion ep : end) {
				e.connectTo(ep);
				if (activeExpansions.contains(ep)) {
					activeExpansions.remove(ep);
				}
			}
		} else {
			Path expnPath = e.getBestPath();
			Tile expnTile = expnPath.lastTile();
			TileInfo info = (TileInfo) expnTile.content();
			Tile root = hRoot;
			if (info.isHorizontal) {
				root = vRoot;
			}
			List<Tile> tiles = root.areaEnumerate(new FreeToRoute(),
					expnTile.y(), expnTile.x(), expnTile.height(), expnTile.width());
			e.removePath(expnPath);
			for (Tile t : tiles) {
				if (!expnPath.contains(t)) {
					Path newPath = expnPath.add(t);
					e.addPath(newPath);
				}
			}
		}
	}

	public Tile getHTileRoot() {
		return hRoot;
	}
	
	public Tile getVTileRoot() {
		return vRoot;
	}

	public List<Expansion> getActiveExpansions() {
		return activeExpansions;
	}
}
